﻿/*
 * User: RSny
 * Date: 5/8/2012
 * Time: 12:36 PM
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Net.NetworkInformation;
using Aspects.PInvoke;

namespace Aspects.Computer.Network
{
    
    public class BluetoothAdapter
    {
        Native.BLUETOOTH_FIND_RADIO_PARAM parameters;
        Native.BLUETOOTH_RADIO_INFO bluetoothInfo;
        Native.BLUETOOTH_DEVICE_INFO deviceInfo;
        IntPtr firstDevice;
        IntPtr firstRadio;                     

        /// <summary>
        /// Describes a bluetoothAdapter. Call getBluetoothRadio() first
        /// </summary>
        public BluetoothAdapter()
        {
            parameters = new Native.BLUETOOTH_FIND_RADIO_PARAM();
            bluetoothInfo = new Native.BLUETOOTH_RADIO_INFO();

            parameters.Initialize();
            bluetoothInfo.Initialize();

        }
        ~BluetoothAdapter()
        {
            if (deviceInfo.fAuthenticated)
                Native.BluetoothRemoveDevice(ref deviceInfo.Address);

            Native.BluetoothFindDeviceClose(firstDevice);
        }

        public string Name { get; set; }
        public string Description { get; set; }
        public string MACAddress { get; set; }
        public string ComPort { get; set; }
        public bool isPresent { get; set; }

        /// <summary>
        /// Checks computer for a bluetooth radio
        /// </summary>
        /// <returns>returns a instance of BluetoothAdapter even if no bluetooth is present.</returns>
        public BluetoothAdapter getBluetoothRadio()
        {
            Console.Write("Searching for Bluetooth radio: ");
            Native.BluetoothFindFirstRadio(ref parameters, out firstRadio);
            UInt32 error = Native.BluetoothGetRadioInfo(firstRadio, ref bluetoothInfo);
            if (error != 0) //0 is success
            {
                Console.WriteLine("Failed");
                Name = "Not Present";
                Description = "Not Present";
                MACAddress = " ";
                ComPort = " ";
                isPresent = false;
                return this;
            }
            else
            {
                Console.WriteLine("Success");
                printBluetoothPointer();
                Description = "Bluetooth Adapter";
                Name = "Present";
                MACAddress = parseMAC(bluetoothInfo.address);
                ComPort = " ";
                isPresent = true;
                return this;
            }
        }

        public void getDevice()
        {
            //TODO check for isBluetoothRadioConnected
            //TODO if it isn't check for Quality tester without using new inquiry
            //TODO if it isn't found first time run 3 consecitive runs with Inquiry = true
            //TODO make virutal com port
            Console.Write("Searching for first device: ");
            var deviceSearchParams = new Native.BLUETOOTH_DEVICE_SEARCH_PARAMS();
            
            deviceSearchParams.Initialize();

            firstDevice = getDevicePointer(false, true, true, true, false);

            for (int i = 0; i < 3; i++)
            {
                if (firstDevice == IntPtr.Zero)
                {
                    Console.WriteLine("Failed");
                    Console.WriteLine("Running new inquiry: ");
                    firstDevice = getDevicePointer(true, true, true, true, true);
                }
            }
            if (firstDevice == IntPtr.Zero)
            {
                Console.WriteLine("Failed");
                Console.WriteLine("Bluetooth didn't detect any devices");
                
            }
            else
            {
                Console.WriteLine("Success");
                while (deviceInfo.Address != 0xB531615B9)
                {
                    Native.BluetoothFindNextDevice(firstDevice, ref deviceInfo);
                    Console.WriteLine("Found device: {0}", deviceInfo.szName);
                }

                Console.Write("Found {0} ", deviceInfo.szName);
                attemptDeviceConnection(deviceInfo, firstDevice);
                Console.WriteLine("- - - - - - - - - - - - - ");
                
            }
        }

        /// <summary>
        /// Gets the handle to a bluetooth device
        /// </summary>
        /// <param name="authenticated">Return devices that have been authenticated</param>
        /// <param name="remembered">Returns devices that are remembered</param>
        /// <param name="unknown">Returns devices that are unknown</param>
        /// <param name="connected">Returns devices that are already connected</param>
        /// <param name="inquiry">Perform a new inquiry of devices</param>
        /// <param name="device">return a pointer to a BLUETOOTH_DEVICE_INFO</param>
        /// <returns>a handle to a bluetooth device</returns>
        private IntPtr getDevicePointer(bool authenticated, bool remembered, bool unknown, bool connected, bool inquiry)
        {
            var deviceSearchParams = new Native.BLUETOOTH_DEVICE_SEARCH_PARAMS();
            deviceInfo = new Native.BLUETOOTH_DEVICE_INFO();
            IntPtr firstDevice;
            deviceSearchParams.Initialize();
            deviceInfo.Initialize();

            deviceSearchParams.fReturnAuthenticated = authenticated;
            deviceSearchParams.fReturnRemembered = remembered;
            deviceSearchParams.fReturnUnknown = unknown;
            deviceSearchParams.fReturnConnected = connected;
            deviceSearchParams.fIssueInquiry = inquiry;
            deviceSearchParams.hRadio = firstRadio;
            deviceSearchParams.cTimeoutMultiplier = 5;

            firstDevice = Native.BluetoothFindFirstDevice(ref deviceSearchParams, ref deviceInfo);

            return firstDevice;
        }

        private void attemptDeviceConnection(Native.BLUETOOTH_DEVICE_INFO device, IntPtr devicePointer)
        {
            Console.Write("trying to connect: ");
            uint success = Native.BluetoothAuthenticateDevice(IntPtr.Zero, firstRadio, ref device, "1234", 4);
            
            switch (success)
            {
                case (uint)Native.ERROR_CODES.SUCCESS:
                case (uint)Native.ERROR_CODES.Already_Connected:
                    Console.WriteLine("Success");
                    createVirtualPort();
                    break;
                default:
                    Console.WriteLine("Failed. Error Code: {0}", success);
                    break;
            }
            //kill connection
        }

        private void createVirtualPort()
        {
            Console.WriteLine("Virtual Port not working yet");
        }

        private void printBluetoothPointer()
        {
            Console.WriteLine("Name:        {0}", bluetoothInfo.szName);
            Console.WriteLine("Address:     {0}", MACAddress);
            Console.WriteLine("Class:       {0}", bluetoothInfo.ulClassOfDevice);
            Console.WriteLine("Subversion:  {0}", bluetoothInfo.lmpSubversion);
            Console.WriteLine("Manufacturer:{0}", bluetoothInfo.manufacturer);
            Console.WriteLine("- - - - - - -");
        }

        private string parseMAC(UInt64 address)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 6; i++)
            {
                UInt64 oneByte = ((ulong)0xFF << (40 - i * 8)) & address;
                byte answer = (byte)(oneByte >> (40 - i * 8));
                sb.Append(answer.ToString("X2"));
                sb.Append(":");
            }
            return sb.ToString().TrimEnd(':');
        }
    }
}